/**
 * cpuhelpers.h
 * Contains all helper macros/inline functions to simplify and standardize
 * individual opcode implementations in opcodes.c
 *
 * @author  Philippe Johnson
 **/

#ifndef __CPUHELPERS_H__
#define __CPUHELPERS_H__

#ifdef _MSC_VER

#pragma warning(disable:4244) /* Conversion from 'u16' to 'u8'... */
#pragma warning(disable:4333) /* Right shift by too large amount, data loss */

#endif

#include "memory.h"
#include "65c816.h"
#include "opcodetables.h"

/** Flag calculation helpers **/
#define CALCN8(R)           (CPU.negativeFlag = ((u8) (R) & 0x70)   >> 7)
#define CALCN16(R)          (CPU.negativeFlag = ((u16)(R) & 0x7000) >> 15)
#define CALCZ8(R)           (CPU.zeroFlag  = ((u8) (R) == 0))
#define CALCZ16(R)          (CPU.zeroFlag  = ((u16)(R) == 0))
#define CALCC8(R)           (CPU.carryFlag = ((R) >>  8) & 1)
#define CALCC16(R)          (CPU.carryFlag = ((R) >> 16) & 1)

/** TODO: Test these overflow detection functions **/
__inline void CALCV8(u8 X, u8 Y, u8 Z) {
    u8 xsign = (X & 0x80) >> 7;
    u8 ysign = (Y & 0x80) >> 7;
    u8 zsign = (Z & 0x80) >> 7;

    CPU.overflowFlag = ((xsign | ysign) ^ zsign) >> (xsign ^ ysign);
}

__inline void CALCV16(u16 X, u16 Y, u16 Z) {
    u8 xsign = (X & 0x8000) >> 7; /* >>15? */
    u8 ysign = (Y & 0x8000) >> 7;
    u8 zsign = (Z & 0x8000) >> 7;

    CPU.overflowFlag = ((xsign | ysign) ^ zsign) >> (xsign ^ ysign);
}

/**
 * Address mode helpers-- return the virtual effective address
 */
__inline u32 amStackRelative() {
    return (CPU.SP + memRead16NoTrap(CPU.PC.PCR + 1)) & 0xFFFF;
}

__inline u32 amStackRelativeIndirectIndexY8() {
    u32 valAtAddress = memRead16NoTrap(amStackRelative());

    return CPU.DBR + valAtAddress + CPU.Y.yl;
}

__inline u32 amStackRelativeIndirectIndexY16() {
    u32 valAtAddress = memRead16NoTrap(amStackRelative());

    return CPU.DBR + valAtAddress + CPU.Y.y;
}

__inline u32 amImmediate() {
    return CPU.PC.PCR + 1;
}

__inline u32 amAbsolute() {
    return (CPU.DBR << 16) | memRead16NoTrap(CPU.PC.PCR + 1);   
}

__inline u32 amAbsoluteLong() {    
    return memRead24NoTrap(CPU.PC.PCR + 1);
}

__inline u32 amAbsoluteIndexX8() {
    return ((CPU.DBR << 16) | (memRead16NoTrap(CPU.PC.PCR + 1) + CPU.X.xl));
}

__inline u32 amAbsoluteIndexX16() {
    return ((CPU.DBR << 16) | (memRead16NoTrap(CPU.PC.PCR + 1) + CPU.X.xl));
}

__inline u32 amAbsoluteIndexY8() {
    return (CPU.DBR << 16) | (memRead16NoTrap(CPU.PC.PCR + 1) + CPU.Y.yl);    
}

__inline u32 amAbsoluteIndexY16() {
    return (CPU.DBR << 16) | (memRead16NoTrap(CPU.PC.PCR + 1) + CPU.Y.y);
}

__inline u32 amAbsoluteIndexIndirect8() {
    return (CPU.PC.PBR << 16) | memRead16NoTrap(amAbsoluteIndexX8());
}

__inline u32 amAbsoluteIndexIndirect16() {
    return (CPU.PC.PBR << 16) | memRead16NoTrap(amAbsoluteIndexX16());
}

__inline u32 amAbsoluteIndirect() {
    return (CPU.PC.PBR << 16) | memRead16NoTrap(amAbsolute());
}

__inline u32 amAbsoluteIndirectLong() {
    return memRead24NoTrap(amAbsoluteLong());
}

__inline u32 amAbsoluteLongIndexX8() {
    return memRead24NoTrap(CPU.PC.PCR + 1) + CPU.X.xl;
}

__inline u32 amAbsoluteLongIndexX16() {
    return memRead24NoTrap(CPU.PC.PCR + 1) + CPU.X.x;
}

__inline u32 amAbsoluteLongIndexY16() {
    return memRead24NoTrap(CPU.PC.PCR + 1) + CPU.Y.y; 
}

__inline u32 amAbsoluteLongIndexY8() {
    return memRead24NoTrap(CPU.PC.PCR + 1) + CPU.Y.yl;   
}

__inline u32 amDirectPage() {
    return (memRead16NoTrap(CPU.PC.PCR + 1) + CPU.DPR) & 0xFFFF;
}

__inline u32 amDirectPageIndexX8() {
    return (memRead16NoTrap(CPU.PC.PCR + 1) + CPU.DPR + CPU.X.xl) & 0xFFFF;    
}

__inline u32 amDirectPageIndexX16() {
    return (memRead16NoTrap(CPU.PC.PCR + 1) + CPU.DPR + CPU.X.x) & 0xFFFF;
}

__inline u32 amDirectPageIndexY8() {
    return (memRead16NoTrap(CPU.PC.PCR + 1) + CPU.DPR + CPU.Y.yl) & 0xFFFF;
}

__inline u32 amDirectPageIndexY16() {
    return (memRead16NoTrap(CPU.PC.PCR + 1) + CPU.DPR + CPU.Y.y) & 0xFFFF;
}

__inline u32 amDirectPageIndexIndirectX8() {
    return (CPU.DPR << 16) | (memRead16NoTrap(amDirectPageIndexX8()));
}

__inline u32 amDirectPageIndexIndirectX16() {
    return (CPU.DPR << 16) | (memRead16NoTrap(amDirectPageIndexX16()));
}

__inline u32 amDirectPageIndirect() {
    return memRead16NoTrap(amDirectPage());
}

__inline u32 amDirectPageIndirectLong() {
    return memRead24NoTrap(amDirectPage());    
}

__inline u32 amDirectPageIndexIndirectY8() {
    return (CPU.DPR << 16) | (memRead16NoTrap(amDirectPageIndexY8()));
}

__inline u32 amDirectPageIndexIndirectY16() {
    return (CPU.DPR << 16) | (memRead16NoTrap(amDirectPageIndexY16()));
}

__inline u32 amDirectPageIndirectIndexY8() {
    return (CPU.DPR << 16) | ((memRead16NoTrap(amDirectPage()) + CPU.Y.yl));
}

__inline u32 amDirectPageIndirectIndexY16() {
    return (CPU.DPR << 16) | ((memRead16NoTrap(amDirectPage()) + CPU.Y.y));
}

__inline u32 amDirectPageIndirectLongIndexY8() {
    return (CPU.DPR << 16) | (memRead24NoTrap(amDirectPageIndexY8()));
}

__inline u32 amDirectPageIndirectLongIndexY16() {
    return (CPU.DPR << 16) | (memRead24NoTrap(amDirectPageIndexY16()));
}

/**
 * Stack helpers
 */
static __inline void PUSH8(u8 value) {
    memWrite8NoTrap(CPU.SP--, value);
}

static __inline void PUSH16(u16 value) {
    memWrite8NoTrap(CPU.SP--, value & 0xFF);
    memWrite8NoTrap(CPU.SP--, value >> 8  );
}

static __inline void PUSH24(u32 value) {
    memWrite8NoTrap(CPU.SP--, value & 0xFF);
    memWrite8NoTrap(CPU.SP--, (value >>  8) & 0xFF );
    memWrite8NoTrap(CPU.SP--, (value >> 16) & 0xFF );
}

static __inline void PULL8(u8* value) {
    *value = memRead8NoTrap(CPU.SP++);
}

static __inline void PULL16(u16* value) {
    *value = memRead16NoTrap(CPU.SP);
    CPU.SP += 2;
}

static __inline void PULL24(u32* value) {
    *value = memRead24NoTrap(CPU.SP);
    CPU.SP += 3;
}

/**
 * P register helpers
 */
static __inline void restorePRegister() {
    CPU.P |= CPU.carryFlag    * CFLAGMASK;
    CPU.P |= CPU.irqFlag      * IFLAGMASK;
    CPU.P |= CPU.negativeFlag * NFLAGMASK;
    CPU.P |= CPU.overflowFlag * VFLAGMASK;
    CPU.P |= CPU.zeroFlag     * ZFLAGMASK;
    CPU.P |= CPU.mFlag        * MFLAGMASK;
    CPU.P |= CPU.xFlag        * XFLAGMASK;
}

static __inline void restorePFlags() {
    CPU.carryFlag    = (CPU.P & CFLAGMASK);
    CPU.zeroFlag     = (CPU.P & ZFLAGMASK) >> 1;
    CPU.irqFlag      = (CPU.P & IFLAGMASK) >> 2;
    CPU.mFlag        = (CPU.P & MFLAGMASK) >> 4;
    CPU.xFlag        = (CPU.P & XFLAGMASK) >> 5;
    CPU.overflowFlag = (CPU.P & VFLAGMASK) >> 6;
    CPU.negativeFlag = (CPU.P & NFLAGMASK) >> 7;
}

/**
 * Cycle and byte counter helpers
 */
__inline void updateCyclesByOpcodeMX(u8 opcode) {
    CPU.cycleCounter -= opCyclesMX[opcode];
}

__inline void updateCyclesByOpcodemX(u8 opcode) {
    CPU.cycleCounter -= opCyclesmX[opcode];
}

__inline void updateCyclesByOpcodeMx(u8 opcode) {
    CPU.cycleCounter -= opCyclesMx[opcode];
}

__inline void updateCyclesByOpcodemx(u8 opcode) {
    CPU.cycleCounter -= opCyclesmx[opcode];
}

__inline void updateCycles(u8 cycles) {
    CPU.cycleCounter -= cycles;
}

__inline void updatePCByOpcodeMX(u8 opcode) {
    CPU.PC.PCR += opSizeMX[opcode];
}

__inline void updatePCByOpcodemX(u8 opcode) {
    CPU.PC.PCR += opSizemX[opcode];
}

__inline void updatePCByOpcodeMx(u8 opcode) {
    CPU.PC.PCR += opSizeMx[opcode];
}

__inline void updatePCByOpcodemx(u8 opcode) {
    CPU.PC.PCR += opSizemx[opcode];    
}

__inline void updatePC(u8 bytes) {
    CPU.PC.PCR += bytes;
}

/**
 * Opcode helpers
 */
__inline void ADC8(u8 value) {
    u16 sum = CPU.A.al + value + CPU.carryFlag;

    CALCV8(value, CPU.A.al, sum);
    CALCN8(sum);
    CALCZ8(sum);
    CALCC8(sum);

    CPU.A.al = (u8)sum & 0xFF;
}

__inline void ADC16(u16 value) {
    u32 sum = CPU.A.ac + value + CPU.carryFlag;

    CALCV16(value, CPU.A.ac, sum);
    CALCN16(sum);
    CALCZ16(sum);
    CALCC16(sum);

    CPU.A.ac = (u16)sum & 0xFFFF;
}

__inline void AND8(u8 value) {
    CPU.A.al &= value;

    CALCN8(CPU.A.al);
    CALCZ8(CPU.A.al);
}

__inline void AND16(u16 value) {
    CPU.A.ac &= value;

    CALCN16(CPU.A.ac);
    CALCZ16(CPU.A.ac);
}

__inline void ASL8(u8* address) {
    CPU.carryFlag = *address >> 7;
    *address <<= 1;

    CALCN8(*address);
    CALCZ8(*address);
}

__inline void ASL16(u16* address) {
    CPU.carryFlag = *address >> 15;
    *address <<= 1;

    CALCN16(*address);
    CALCZ16(*address);
}

__inline void BCC() {
    s8 offset   = memRead8NoTrap(CPU.PC.PCR + 1);
    u8 jump     = ~CPU.carryFlag & 1;
    CPU.PC.PCR += offset * jump;
}

__inline void BCS() {
    s8 offset = memRead8NoTrap(CPU.PC.PCR + 1);
    CPU.PC.PCR += offset * CPU.carryFlag;
}

__inline void BEQ() {
    s8 offset = memRead8NoTrap(CPU.PC.PCR + 1);
    CPU.PC.PCR += offset * CPU.zeroFlag;
}

__inline void BIT8(s8 value) {
    u8 tempValue;
    CPU.negativeFlag = value >> 7;
    CPU.overflowFlag = (value >> 6) & 1;

    tempValue = value & CPU.A.al;
    CALCZ8(tempValue);
}

__inline void BIT16(s16 value) {
    u16 tempValue;
    CPU.negativeFlag = value >> 15;
    CPU.overflowFlag = (value >> 14) & 1;

    tempValue = value & CPU.A.ac;
    CALCZ16(tempValue);
}

__inline void BMI() {
    s8 offset   = memRead8NoTrap(CPU.PC.PCR + 1);
    CPU.PC.PCR += offset * CPU.negativeFlag;
}

__inline void BNE() {
    s8 offset   = memRead8NoTrap(CPU.PC.PCR + 1);
    u8 jump     = ~CPU.zeroFlag & 1;
    CPU.PC.PCR += offset * jump;
}

__inline void BPL() {
    s8 offset   = memRead8NoTrap(CPU.PC.PCR + 1);
    u8 jump     = ~CPU.negativeFlag & 1;
    CPU.PC.PCR += offset * jump;
}

__inline void BRA() {
    s8 offset   = memRead8NoTrap(CPU.PC.PCR + 1);
    CPU.PC.PCR += offset;
}

__inline void BRK() {
    CPU.PC.PCR += 2;
    PUSH24(CPU.PC.PCR);

    restorePRegister();
    PUSH8(CPU.P);

    CPU.irqFlag = 1;          /* Disable interrupts        */
    CPU.PC.PBR  = 0;          /* Clear the program bank    */
    CPU.P      &= ~DFLAGMASK; /* Clear the D flag */

    CPU.PC.PCR  = memRead16NoTrap(CPU.breakVector);
}

__inline void BRL() {
    s16 offset  = memRead16NoTrap(CPU.PC.PCR + 1);
    CPU.PC.PCR += offset;
}

__inline void BVC() {
    s8 offset   = memRead8NoTrap(CPU.PC.PCR + 1);
    u8 jump     = ~CPU.overflowFlag & 1;
    CPU.PC.PCR += jump * offset;
}

__inline void BVS() {
    s8 offset   = memRead8NoTrap(CPU.PC.PCR + 1);
    CPU.PC.PCR += offset * CPU.overflowFlag;
}

__inline void CLC() {
    CPU.carryFlag = 0;
}

__inline void CLD() {
    CPU.P &= ~DFLAGMASK;
}

__inline void CLI() {
    CPU.P &= ~IFLAGMASK;
}

__inline void CLV() {
    CPU.overflowFlag = 0;
}

__inline void CMP8(u8 value) {
    u16 tempValue = CPU.A.al - value;

    CALCV8(CPU.A.al, value, tempValue);
    CALCN8(tempValue);
    CALCZ8(tempValue);
}

__inline void CMP16(u16 value) {
    u32 tempValue = CPU.A.ac - value;

    CALCV16(CPU.A.ac, value, tempValue);
    CALCN16(tempValue);
    CALCZ16(tempValue);
}

__inline void COP() {
    CPU.PC.PCR += 2;
    PUSH24(CPU.PC.PCR);

    restorePRegister();
    PUSH8(CPU.P);

    CPU.irqFlag = 1;          /* Disable interrupts        */
    CPU.PC.PBR  = 0;          /* Clear the program bank    */
    CPU.P      &= ~DFLAGMASK; /* Clear the D flag */

    CPU.PC.PCR  = memRead16NoTrap(CPU.copVector);
}

__inline void CPX8(u8 value) {
    u16 tempValue = CPU.X.xl - value;

    CALCV8(CPU.X.xl, value, tempValue);
    CALCN8(tempValue);
    CALCZ8(tempValue);    
}

__inline void CPX16(u16 value) {
    u32 tempValue = CPU.X.x - value;

    CALCV16(CPU.X.x, value, tempValue);
    CALCN16(tempValue);
    CALCZ16(tempValue);
}

__inline void CPY8(u8 value) {
    u16 tempValue = CPU.Y.yl - value;

    CALCV8(CPU.Y.yl, value, tempValue);
    CALCN8(tempValue);
    CALCZ8(tempValue);    
}

__inline void CPY16(u16 value) {
    u32 tempValue = CPU.Y.y - value;

    CALCV16(CPU.Y.y, value, tempValue);
    CALCN16(tempValue);
    CALCZ16(tempValue);
}

__inline void DEC8(u8* value) {
    u16 tempValue = --(*value);

    CALCV8(*value, -1, tempValue);
    CALCN8(tempValue);
    CALCZ8(tempValue);
}

__inline void DEC16(u16* value) {
    u32 tempValue = --(*value);

    CALCV16(*value, -1, tempValue);
    CALCN16(tempValue);
    CALCZ16(tempValue);
}

__inline void DEX8() {
    --CPU.X.xl;

    CALCN8(CPU.X.xl);
    CALCZ8(CPU.X.xl);
}

__inline void DEX16() {
    --CPU.X.x;

    CALCN16(CPU.X.x);
    CALCZ16(CPU.X.x);
}

__inline void DEY8() {
    --CPU.Y.yl;

    CALCN8(CPU.Y.yl);
    CALCZ8(CPU.Y.yl);
}

__inline void DEY16() {
    --CPU.Y.y;

    CALCN16(CPU.Y.y);
    CALCZ16(CPU.Y.y);
}

__inline void EOR8(u8 value) {
    CPU.A.al ^= value;

    CALCN8(CPU.A.al);
    CALCZ8(CPU.A.al);
}

__inline void EOR16(u16 value) {
    CPU.A.ac ^= value;

    CALCN16(CPU.A.ac);
    CALCZ16(CPU.A.ac);
}

__inline void INC8(u8* value) {
    ++(*value);

    CALCN8(*value);
    CALCZ8(*value);
}

__inline void INC16(u16* value) {
    ++(*value);

    CALCN16(*value);
    CALCZ16(*value);
}

__inline void INX8() {
    ++CPU.X.xl;

    CALCN8(CPU.X.xl);
    CALCZ8(CPU.X.xl);
}

__inline void INX16() {
    ++CPU.X.x;

    CALCN16(CPU.X.x);
    CALCZ16(CPU.X.x);
}

__inline void INY8() {
    ++CPU.Y.yl;

    CALCN8(CPU.Y.yl);
    CALCZ8(CPU.Y.yl);
}

__inline void INY16() {
    ++CPU.Y.y;

    CALCN16(CPU.Y.y);
    CALCZ16(CPU.Y.y);
}

__inline void JMP(u16 value) {
    CPU.PC.PCL = value;
}

__inline void JML(u32 value) {
    CPU.PC.PCR = value & 0xFFFFFF;
}

__inline void JSR(u16 value) {
    PUSH8(CPU.PC.PCL >> 8);  /* High byte first */
    PUSH8(CPU.PC.PCL & 0xFF);/* Low  byte next  */

    CPU.PC.PCL = value;
}

__inline void JSL(u32 value) {
    PUSH8(CPU.PC.PBR);
    PUSH8(CPU.PC.PCL >> 8);  /* High byte first */
    PUSH8(CPU.PC.PCL & 0xFF);/* Low  byte next  */

    CPU.PC.PCR = value & 0xFFFFFF;
}

__inline void LDA8(u8 value) {
    CPU.A.al = value;

    CALCN8(CPU.A.al);
    CALCZ8(CPU.A.al);
}

__inline void LDA16(u16 value) {
    CPU.A.ac = value;

    CALCN16(CPU.A.ac);
    CALCZ16(CPU.A.ac);
}

__inline void LDX8(u8 value) {
    CPU.X.xl = value;

    CALCN8(CPU.X.xl);
    CALCZ8(CPU.X.xl);
}

__inline void LDX16(u16 value) {
    CPU.X.x = value;

    CALCN16(CPU.X.x);
    CALCZ16(CPU.X.x);
}

__inline void LDY8(u8 value) {
    CPU.Y.yl = value;

    CALCN8(CPU.Y.yl);
    CALCZ8(CPU.Y.yl);
}

__inline void LDY16(u16 value) {
    CPU.Y.y = value;

    CALCN16(CPU.Y.y);
    CALCZ16(CPU.Y.y);
}

__inline void LSR8(u8* value) {
    CPU.carryFlag = *value & 1;
    *value >>= 1;

    CPU.negativeFlag = 0;
    
    CALCZ8(*value);
}

__inline void LSR16(u16* value) {
    CPU.carryFlag = *value & 1;
    *value >>= 1;

    CPU.negativeFlag = 0;

    CALCZ16(*value);
}

__inline void MVN(u8 srcBank, u8 dstBank) {
    /*
    TODO: Finish this instruction

    u8* src = (u8*)GETADDRESS((srcBank << 16) | CPU.X.x);
    u8* dst = (u8*)GETADDRESS((dstBank << 16) | CPU.Y.y);

    memcpy(dst, src, CPU.A.ac + 1);

    CPU.X.x += CPU.A.ac + 1;
    CPU.Y.y += CPU.A.ac + 1;

    CPU.A.ac = 0xFFFF;
    CPU.DBR  = dstBank;

    Implementation note: Keep interrupts in mind-- need to be able to
    start moving a block, jump to an interrupt vector and resume the block
    transfer...
    */
}

__inline void MVP(u8 srcBank, u8 dstBank) {
    /* TODO. Check implementation note above (same applies) */
}

__inline void NOP() {
    return;
}

__inline void ORA8(u8 value) {
    CPU.A.al |= value;

    CALCN8(CPU.A.al);
    CALCZ8(CPU.A.al);
}

__inline void ORA16(u16 value) {
    CPU.A.ac |= value;

    CALCN16(CPU.A.ac);
    CALCZ16(CPU.A.ac);
}

__inline void PEA(u16 value) {
    PUSH16(value);
}

__inline void PEI(u16 value) {
    PUSH16(value);
}

__inline void PER(s16 value) {
    u16 tempValue = (s16)CPU.PC.PCL + value;

    /*PUSH8(tempValue);*/
    PUSH8(tempValue >> 8);  /* High byte first */
    PUSH8(tempValue & 0xFF);/* Low byte next   */
}

__inline void PHA8() {
    PUSH8(CPU.A.al);
}

__inline void PHA16() {
    PUSH8(CPU.A.ah);
    PUSH8(CPU.A.al);
}

__inline void PHB() {
    PUSH8(CPU.DBR);
}

__inline void PHD() {
    PUSH8(CPU.DPR);
}

__inline void PHK() {
    PUSH8(CPU.PC.PBR);
}

__inline void PHP() {
    restorePRegister();

    PUSH8(CPU.P);
}

__inline void PHX8() {
    PUSH8(CPU.X.xl);
}

__inline void PHX16() {
    PUSH8(CPU.X.xh);
    PUSH8(CPU.X.xl);
}

__inline void PHY8() {
    PUSH8(CPU.Y.yl);
}

__inline void PHY16() {
    PUSH8(CPU.Y.yh);
    PUSH8(CPU.Y.yl);
}

__inline void PLA8() {
    PULL8(&CPU.Y.yl);
}

__inline void PLA16() {
    PULL16(&CPU.Y.y);
}

__inline void PLB() {
    PULL8(&CPU.DBR);
}

__inline void PLD() {
    PULL8((u8*)(&CPU.DPR));
}

__inline void PLP() {
    PULL8(&CPU.P);
    restorePFlags();
}

__inline void PLX8() {
    PULL8(&CPU.X.xl);
}   

__inline void PLX16() {
    PULL16(&CPU.X.x);
}

__inline void PLY8() {
    PULL8(&CPU.Y.yl);
}   

__inline void PLY16() {
    PULL16(&CPU.Y.y);
}

__inline void REP(u8 value) {
    CPU.P &= ~value;
    restorePFlags();
}

__inline void ROL8(u8* value) {
    CPU.carryFlag = (*value & 0x80) >> 7;
    *value <<= 1;
    *value  |= CPU.carryFlag;

    CALCN8(*value);
    CALCZ8(*value);
}

__inline void ROL16(u16* value) {
    CPU.carryFlag = (*value & 0x8000) >> 15;
    *value <<= 1;
    *value  |= CPU.carryFlag;

    CALCN16(*value);
    CALCZ16(*value);
}

__inline void ROR8(u8* value) {
    CPU.carryFlag = *value & 1;
    *value >>= 1;
    *value  |= (CPU.carryFlag << 7);

    CALCN8(*value);
    CALCZ8(*value);
}

__inline void ROR16(u16* value) {
    CPU.carryFlag = *value & 1;
    *value >>= 1;
    *value  |= (CPU.carryFlag << 15);

    CALCN16(*value);
    CALCZ16(*value);
}

__inline void RTI() {
    PULL24(&CPU.PC.PCR);
    PULL8(&CPU.P);
    restorePFlags();
}

__inline void RTL() {
    PULL24(&CPU.PC.PCR);

    ++CPU.PC.PCR;
}

__inline void RTS() {
    PULL16(&CPU.PC.PCL);

    ++CPU.PC.PCR;
}

__inline void SBC8(u8 value) {
    u16 tempValue = CPU.A.al - value - CPU.carryFlag;

    CALCN8(tempValue);
    CALCV8(CPU.A.al, -(value + CPU.carryFlag), tempValue);
    CALCZ8(tempValue);
    CALCC8(~tempValue);/* Carry CLEARS if no borrow was required! */

    CPU.A.al = tempValue;
}

__inline void SBC16(u16 value) {
    u16 tempValue = CPU.A.ac - value - CPU.carryFlag;

    CALCN16(tempValue);
    CALCV16(CPU.A.al, -(value + CPU.carryFlag), tempValue);
    CALCZ16(tempValue);
    CALCC16(~tempValue);/* Carry CLEARS if no borrow was required! */

    CPU.A.ac = tempValue;
}

__inline void SEC() {
    CPU.carryFlag = 1;
}

__inline void SED() {
    CPU.P |= DFLAGMASK;
}

__inline void SEI() {
    CPU.irqFlag = 1;
}

__inline void SEP(u8 value) {
    CPU.P |= value;
    restorePFlags();
}

__inline void STA8(u8* effectiveAddr) {
    *effectiveAddr = CPU.A.al;
}

__inline void STA16(u16* effectiveAddr) {
    *effectiveAddr = CPU.A.ac;
}

__inline void STP() {
    /* TODO */
}

__inline void STX8(u8* effectiveAddr) {
    *effectiveAddr = CPU.X.xl;
}

__inline void STX16(u16* effectiveAddr) {
    *effectiveAddr = CPU.X.x;
}

__inline void STY8(u8* effectiveAddr) {
    *effectiveAddr = CPU.Y.yl;
}

__inline void STY16(u16* effectiveAddr) {
    *effectiveAddr = CPU.Y.y;
}

__inline void STZ8(u8* effectiveAddr) {
    *effectiveAddr = 0;
}

__inline void STZ16(u16* effectiveAddr) {
    *effectiveAddr = 0;
}

__inline void TAX8() {
    CPU.X.xl = CPU.A.al;

    CALCN8(CPU.X.xl);
    CALCZ8(CPU.X.xl);
}

__inline void TAX16() {
    CPU.X.x = CPU.A.ac;

    CALCN16(CPU.X.x);
    CALCZ16(CPU.X.x);
}

__inline void TAY8() {
    CPU.Y.yl = CPU.A.al;

    CALCN8(CPU.Y.yl);
    CALCZ8(CPU.Y.yl);
}

__inline void TAY16() {
    CPU.Y.y = CPU.A.ac;

    CALCN16(CPU.Y.yl);
    CALCZ16(CPU.Y.yl);
}

__inline void TCD() {
    CPU.DPR = CPU.A.ac;

    CALCN16(CPU.A.ac);
    CALCZ16(CPU.A.ac);
}

__inline void TCS() {
    CPU.SP = CPU.A.ac;

    CALCN16(CPU.A.ac);
    CALCZ16(CPU.A.ac);
}

__inline void TDC() {
    CPU.A.ac = CPU.DPR;

    CALCN16(CPU.A.ac);
    CALCZ16(CPU.A.ac);
}

__inline void TRB8(u8* value) {
    *value &= ~CPU.A.al;

    CALCZ8(*value);
}

__inline void TRB16(u16* value) {
    *value &= ~CPU.A.ac;

    CALCZ16(*value);
}

__inline void TSB8(u8* value) {
    *value |= CPU.A.al;

    CALCZ8(*value);
}

__inline void TSB16(u16* value) {
    *value |= CPU.A.ac;

    CALCZ16(*value);
}

__inline void TSC() {
    CPU.A.ac = CPU.SP;

    CALCN16(CPU.A.ac);
    CALCZ16(CPU.A.ac);
}

__inline void TSX8() {
    CPU.X.xl = (u8)CPU.SP;

    CALCN8(CPU.X.xl);
    CALCZ8(CPU.X.xl);
}

__inline void TSX16() {
    CPU.X.x = CPU.SP;

    CALCN16(CPU.X.x);
    CALCZ16(CPU.X.x);
}

__inline void TXA8() {
    CPU.A.al = CPU.X.xl;

    CALCN8(CPU.A.al);
    CALCZ8(CPU.A.al);
}

__inline void TXA16() {
    CPU.A.ac = CPU.X.x;

    CALCN16(CPU.A.ac);
    CALCZ16(CPU.A.ac);
}

__inline void TXY8() {
    CPU.Y.yl = CPU.X.xl;

    CALCN8(CPU.Y.yl);
    CALCZ8(CPU.Y.yl);
}

__inline void TXY16() {
    CPU.Y.y = CPU.X.x;

    CALCN8(CPU.Y.y);
    CALCZ8(CPU.Y.y);
}

__inline void TYX8() {
    CPU.X.xl = CPU.Y.yl;

    CALCN8(CPU.Y.yl);
    CALCZ8(CPU.Y.yl);
}

__inline void TYX16() {
    CPU.X.x = CPU.Y.y;

    CALCN8(CPU.Y.y);
    CALCZ8(CPU.Y.y);
}

__inline void TXS8() {
    CPU.SP = CPU.X.xl;
}

__inline void TXS16() {
    CPU.SP = CPU.X.x;
}

__inline void TYA8() {
    CPU.A.al = CPU.Y.yl;

    CALCN8(CPU.A.al);
    CALCZ8(CPU.A.al);
}

__inline void TYA16() {
    CPU.A.ac = CPU.Y.y;

    CALCN16(CPU.A.ac);
    CALCZ16(CPU.A.ac);
}

__inline void WAI() {
    /* No purpose here */
    return;
}

__inline void WDM() {
    /* "Reserved for future expansion" */
    return;
}

__inline void XBA() {
    u8 temp;

    /* Swap */
    temp = CPU.A.al;
    CPU.A.al = CPU.A.ah;
    CPU.A.ah = temp;

    CALCN8(CPU.A.al);
    CALCZ8(CPU.A.al);
}

__inline void XCE() {
    u8 temp;

    /* Swap */
    temp = CPU.carryFlag;
    CPU.carryFlag = CPU.emuFlag;
    CPU.emuFlag = temp;
}

#endif // __CPUHELPERS_H__
